package com.zjp.lock.redis;

import com.sun.org.apache.regexp.internal.REUtil;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.Transaction;

import java.util.List;
import java.util.UUID;

public class RedisDistributedLock {

    private final JedisPool jedisPool;

    private final String LOCK_PREFIX = "redis-distributed-lock";
    public RedisDistributedLock(JedisPool jedisPool) {
        this.jedisPool = jedisPool;
    }

    /**
     *
     * @param lockName  锁名称
     * @param acquireTimeout 获取锁超时时间，超时后返回null
     * @param timeout 锁超时时间
     * @return
     */
    public String tryLock(String lockName,long acquireTimeout,long timeout){
        Jedis jedis = null;
        try {
            jedis =  jedisPool.getResource();
            String identifier = UUID.randomUUID().toString();

            String lockKey = LOCK_PREFIX + lockName;

            // 超时时间，上锁后超过此时间则自动释放锁
            int lockExpire = (int)(timeout/1000);
            // 获取锁的超时时间，超过这个时间则放弃获取锁
            long acquireEndTime = System.currentTimeMillis() + acquireTimeout;
            while (System.currentTimeMillis() < acquireEndTime){
                // SETNX key val：当且仅当key不存在时，set一个key为val的字符串，返回1；若key存在，则什么都不做，返回0
                if(jedis.setnx(lockKey,identifier) == 1){
                    jedis.expire(lockKey,lockExpire);
                    return identifier;
                }

                if(jedis.ttl(lockKey) == -1){
                    jedis.expire(lockKey,lockExpire);
                }
                try {
                    Thread.sleep(10);
                }catch (InterruptedException e){
                    Thread.currentThread().interrupt();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if(jedis != null){
                jedis.close();
            }
        }
        return null;
    }


    public boolean releaseLock(String lockName,String identifier){
        Jedis jedis = null;
        String lockKey = LOCK_PREFIX + lockName;
        try {
            jedis = jedisPool.getResource();
            while(true){
                jedis.watch(lockKey);
                if(identifier.equals(jedis.get(lockKey))){
                    Transaction transaction = jedis.multi();
                    transaction.del(lockKey);
                    List<Object> result = transaction.exec();
                    if(result == null){
                        continue;
                    }
                }
                jedis.unwatch();
                return true;
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if(jedis != null){
                jedis.close();
            }
        }
        return false;
    }
}
